import pandas as pd
from sklearn.model_selection import train_test_split
import warnings
warnings.filterwarnings('ignore')
#1.读入数据
df = pd.read_csv('train_titanic.csv')

#2.数据检视()
print("df.head()\n",df.head())
print("df.info()")
df.info()
print("df.describe()\n",df.describe())

#3.训练集 测试集 拆开
train_df,test_df = train_test_split(df)
#增加一列属性 表明 是 训练集 还是 测试集
train_df['traintest']='train'
test_df['traintest']='test'
#同时处理 训练集和测试集，合并训练集 和 测试集
all_df = pd.concat([train_df,test_df])

#查看有缺失值的列索引即 属性索引
print("pd.isnull(all_df)")
print(pd.isnull(all_df))

print("pd.isnull(all_df).sum")
print(pd.isnull(all_df).sum())

print('all_df.columns')
print(all_df.columns)

print('all_df.columns[pd.isnull(all_df).sum()>0]')
print(all_df.columns[pd.isnull(all_df).sum()>0])

#判断 id列 是否真的是主键列
#观察PassengerId数据的变化，发现是一个主键列，所以根据实际情况，主键列不能作为数据进行模型计算，会影响模型精度
print(all_df.PassengerId.value_counts().sort_values)

# 二、数据分析
# 2.1按照船票等级进行分析
# 查看船票等级，1等和2等票的人数相对较少
print("all_df.pclass.value_count()")
print(all_df.Pclass.value_counts())

#查看训练集中各类别座舱遇难人数
#1.Pclass 列 中 筛选出 没有生还的 具体船舱等级
train_df.Pclass[train_df.Survived==0]
#频数统计
print(train_df.Pclass[train_df.Survived==0].value_counts())
s0=train_df.Pclass[train_df.Survived==0].value_counts()
print("s0\n",s0)
print("type(s0)",type(s0))

#计算 不同舱位 没有 生还的 比例
print(train_df.Pclass[train_df.Survived==0].value_counts()/all_df.Pclass.value_counts())

# 查看训练集中各类别座舱幸存人数
s1=train_df.Pclass[train_df.Survived==1].value_counts()

df2=pd.DataFrame({u'未获救':s0,u"获救":s1})
print("df2")
print(df2)

df2.plot(kind='bar')
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['font.sans-serif']=['SimHei']
matplotlib.rcParams['axes.unicode_minus']=False
plt.title("各船舱获救情况统计")
plt.xlabel("船舱等级")
plt.ylabel("人数")
plt.show()

# 2.2按照姓名进行分析
# 分析名字，提取称呼，根据称呼可以大致的判断性别年龄等。还可以提取姓氏
print(all_df.Name.head(10))

# 使用正则化处理，找出每个人名字之中的称呼
import numpy as np
import re
#Staneff, Mr. Ivan
regx = re.compile('(.*, )|(\..*)')
print("all_df.Name")
print(all_df.Name)
print("all_df.Name.values")
print(all_df.Name.values)

title=[]
for name in all_df.Name.values:
    one_title=re.sub(regx,'',name)
    title.append(one_title)
all_df['title']=title
print("修改前all_df['title'].value_counts()\n",all_df['title'].value_counts())

#将一些相关的称呼进行统计合并
#1.将 Mlle 行的 title列（属性） 改成Miss
# all_df.loc[all_df.title=='Mlle','title']='Miss'
all_df.loc[(all_df.title=='Mlle')|(all_df.title=='Ms'),'title']='Miss'
all_df.loc[all_df.title=='Mme','title']='Mrs'

print("修改1后all_df['title'].value_counts()\n",all_df['title'].value_counts())
#2 将常见称呼以外的其他称呼单独创建为稀有称呼 处理完成这些称呼以后，就可将这些称呼做出非常好的离散化效果
common =["Mr","Miss","Mrs","Master"]
print('all_df.title.isin(common)')
print(all_df.title.isin(common))
all_df.loc[~all_df.title.isin(common),'title']='rare'
print("修改2后all_df['title'].value_counts()\n",all_df['title'].value_counts())

#2.3按照性别进行分析
# 观察性别获救情况，可以发现，性别之间存在很大差异，是一个不错的特征

#1.将训练集 中，生存的样本，根据 性别 不同 ，进行 频数统计
# 男 100 女 50
s = train_df.Sex[train_df.Survived==1]
print("s\n",s)
s1=s.value_counts()
print("s1\n",s1)

##2.将训练集 中，没能生存的样本，根据 性别 不同 ，进行 频数统计
# 男 100 女 50
s = train_df.Sex[train_df.Survived==0]
print("s\n",s)
s0=s.value_counts()
print("s0\n",s0)

#将 保存 获救和未获救两个 系列，封装成 数据帧
df3=pd.DataFrame({u'未获救':s0,u'获救':s1})
print("df3\n",df3)
df3.plot(kind='bar')
plt.title(u"性别获救情况")
plt.xlabel(u"性别")
plt.ylabel(u"人数")
plt.show()

all_df.loc[all_df.Age.isnull()==False,'Age'].plot(kind='kde')
plt.show()

#新增一列 在船上的 家族总人数 ：1(乘客本人)+兄弟姐妹数量+父母孩子数量
all_df['family_cnt']=1+all_df['SibSp']+all_df['Parch']
print("all_df['family_cnt'].value_counts()\n",all_df['family_cnt'].value_counts())

print("原先没有family_type(家族规模)这一列all_df.columns",all_df.columns)
all_df.loc[all_df['family_cnt']==1,"family_type"]='sigle'
all_df.loc[all_df['family_cnt'].isin([2,3]),"family_type"]='middle'
all_df.loc[all_df['family_cnt']>=4,"family_type"]='big'
print("对family_type家庭规模做 频数统计")
print(all_df.family_type.value_counts())

#分析票价的概率密度图(票价的分布)
all_df.Fare.plot(kind='kde')
plt.show()

#图像不方便查看，查看describe对Fare的描述 查看缺失值，以及使用 中位数 填充缺失值
all_df.describe()

#将缺失的票价 Fare 使用 中位数 进行 田中
all_df.loc[all_df.Fare.isnull(),'Fare']=14.45
print("all_df.Fare.value_counts()",all_df.Fare.value_counts())

# 3.5Cabin客舱处理
#1. 观察发现 Cabin 由船舱类型（第一字字母）和船舱号 组成
#因此 将 第一个字母 提取出来 作为 船舱 类型
# 其中 将缺失的数据定义为n （null->'n','u','l','l'->n)
carbin_type=[]
for carbin in all_df.Cabin.values:
        # C234
    # 'C','2','3','4'
    carbin_type.append(str(carbin)[0])
#在数据帧中 新增一列 carbin_type
all_df['cabin_type']=carbin_type
print(all_df.cabin_type.value_counts())

# G，T类别较少，可以直接划分到一个类别中，起名为O
# 只有几条数据模型是无法学习到想要的内容
all_df.loc[all_df.cabin_type=='G','cabin_type']='O'
all_df.loc[all_df.cabin_type=='T','cabin_type']='O'
all_df.cabin_type.value_counts()

#查看船舱类别 对 生存 的影响
s0=all_df.cabin_type[all_df.Survived == 0].value_counts()
s1=all_df.cabin_type[all_df.Survived == 1].value_counts()
df=pd.DataFrame({u'获救':s1, u'未获救':s0})
df.plot(kind='bar')
plt.title(u"客舱号码类型获救情况")
plt.xlabel(u"客舱号码类型")
plt.ylabel(u"人数")
plt.show()

# 3.6Embarked登陆港口缺失值填充处理方式
# 统计频数，观察是否包含缺失值
all_df.Embarked.value_counts()
# 缺失值数量较少，可以直接使用众数填充

all_df.loc[all_df.Embarked.isnull(),'Embarked']='S'
all_df.info()

# 数据之间也存在很大差距，是一个不错的特征
# 不同的 登船港口中  没有生还人数：生还人数 有比较大的 区别 2:1,1:2，1:1
s0=train_df.Embarked[train_df.Survived == 0].value_counts()
s1=train_df.Embarked[train_df.Survived == 1].value_counts()
df=pd.DataFrame({u'获救':s1, u'未获救':s0})
df.plot(kind='bar')
plt.title(u"登录港口获救情况")
plt.xlabel(u"登录港口类型")
plt.ylabel(u"人数")
plt.show()

print("特征工程前查看")
all_df.info()


#特征工程
#1 将 要 使用的离散型 属性/特征 进行 独热编码
#['Pclass','Sex','Embarked','title','family_type','cabin_type']
all_df = pd.get_dummies(all_df,columns=['Pclass','Sex','Embarked','title','family_type','cabin_type'])
all_df.info()
# PassengerId,Survived,Pclass,Name,Sex,Age, SibSp,Parch,Ticket,Fare,Cabin,Embarked

#4.2针对连续型数据，使用标准化进行处理操作

from sklearn.preprocessing import StandardScaler
#1.对 票价 进行 标准化
# ss = StandardScaler()
# all_df.Fare = ss.fit_transform(all_df.Fare)
scaler = StandardScaler()
age_scale_param = scaler.fit(all_df[['Fare']])
all_df['Fare_scaled'] = scaler.fit_transform(all_df[['Fare']], age_scale_param)

age_scale_param = scaler.fit(all_df[['family_cnt']])
all_df['family_cnt_scaled'] = scaler.fit_transform(all_df[['family_cnt']], age_scale_param)

age_scale_param = scaler.fit(all_df[['SibSp']])
all_df['SibSp_scaled'] = scaler.fit_transform(all_df[['SibSp']], age_scale_param)

age_scale_param = scaler.fit(all_df[['Parch']])
all_df['Parch_scaled'] = scaler.fit_transform(all_df[['Parch']], age_scale_param)

#查看 数据帧中的 列名称
print(all_df.columns)

# 4.3年龄预测相关特征
# 年龄缺失值过高，采用缺失值的方式进行处理
# 找到和年龄相关的特征，用来预测年龄

# 年龄预测相关特征
age_factor=[ 'Pclass_1', 'Pclass_2',
       'Pclass_3', 'Sex_female', 'Sex_male', 'Embarked_C', 'Embarked_Q',
       'Embarked_S', 'title_Master', 'title_Miss', 'title_Mr', 'title_Mrs',
       'title_rare', 'family_type_big', 'family_type_middle',
       'family_type_sigle', 'cabin_type_A', 'cabin_type_B', 'cabin_type_C',
       'cabin_type_D', 'cabin_type_E', 'cabin_type_F', 'cabin_type_O',
       'cabin_type_n', 'Fare_scaled', 'family_cnt_scaled', 'SibSp_scaled',
       'Parch_scaled']

all_df.info()
# 切分出所有年龄缺失的数据，选择所有年龄相关特征作为测试集特征数据
agenull_test=all_df.loc[all_df.Age.isnull(),age_factor]#X_test

# 切分出年龄不缺失的数据， 选择所有特征作为训练集特征数据
agenotnull_train= all_df.loc[~all_df.Age.isnull(),age_factor]#X_train

# 切分出年龄不缺失的数据， 将所有age作为标签
agenotnull_label=all_df.loc[all_df.Age.notnull(),'Age']
print("特征工程结束")

#使用 线性模型预测 年龄
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(agenotnull_train,agenotnull_label)
age_pre=lr.predict(agenull_test)

all_df.loc[all_df.Age.isnull(),'Age']=age_pre

all_df.info()

#删除无用信息
all_df.pop('Name')
all_df.pop('Ticket')
all_df.pop('Cabin')
print('删除无用信息')

#4.4将年龄进行分段处理
all_df.loc[all_df.Age<=12,'Age_type']='child'
all_df.loc[all_df.Age.between(13,40),'Age_type']='adult'
all_df.loc[all_df.Age>40,'Age_type']='old'

all_df.info()

#4.5年龄分段后进行独热编码处理
all_df=pd.get_dummies(all_df,columns=['Age_type'])

all_df['Age_scaled'] = scaler.fit_transform(all_df[['Age']], age_scale_param)

all_df.info()

all_df.pop('Age')
all_df.pop('SibSp')
all_df.pop('Parch')
all_df.pop('Fare')
print('-')

all_df.info()

#4.7对所有数据进行最终的处理，得到有效数据
train_data=all_df[all_df.traintest=='train']
test_data=all_df[all_df.traintest=='test']

test_data.pop('traintest')
train_data.pop('traintest')
print('-')

test_data.info()
train_data.info()

# 五、模型选择和评估
#5.1 x，y切分
# train_lable=y_train;
y_train = train_data.pop('Survived')

train_data.pop('PassengerId')
X=train_data.values
y=y_train.values

# 5.2逻辑回归计算
# 采用逻辑回归进行训练，使用gridsearchcv进行交叉验证选取最优的超参数。

from sklearn import linear_model
from sklearn.model_selection import GridSearchCV
from sklearn import linear_model
# C正则化系数的倒数  penalty 正则化方式 elasticnet  none
parameters = {'penalty': ('l1', 'l2'),
                  'C': [0.01,0.1,1,10,20]
              }
estimator = linear_model.LogisticRegression()
# 使用正则进行处理
gsearch = GridSearchCV(estimator, param_grid=parameters, scoring='precision', cv=10)
gsearch.fit(X=X,y=y )
print(gsearch.best_params_)
print(gsearch.best_score_)

clf_lr=linear_model.LogisticRegression(C=0.01, penalty='l2', tol=1e-6)
clf_lr.fit(X=X,y=y)

#

